package com.hb.umsp.service;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.hb.umsp.entity.HbExam;
import com.hb.umsp.entity.HbExamCreateType;
import com.hb.umsp.entity.HbExamDetail;
import com.hb.umsp.entity.HbQuestionBank;
import com.hb.umsp.mapper.HbExamCreateTypeMapper;
import com.hb.umsp.mapper.HbExamDetailMapper;
import com.hb.umsp.mapper.HbExamMapper;
import com.hb.umsp.mapper.HbQuestionBankMapper;
import com.hb.umsp.util.ByteArrayUtils;
import com.hb.umsp.util.MaxNoUtils;
import com.hb.umsp.util.token.TokenUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisStringCommands;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.types.Expiration;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Random;

@Service
public class ExamService {

    @Autowired
    RedisTemplate redisTemplate;

    @Autowired
    HbQuestionBankMapper hbQuestionBankMapper;

    @Autowired
    HbExamCreateTypeMapper hbExamCreateTypeMapper;

    @Autowired
    TokenUtil tokenUtil;

    @Autowired
    HbExamMapper hbExamMapper;

    @Autowired
    HbExamDetailMapper hbExamDetailMapper;

    @Autowired
    MaxNoUtils maxNoUtils;

    public PageInfo<HbExam> selectExam(HbExam exam, String token, int pageNum, int pageSize){

        if (exam == null) {
            exam = new HbExam();
        }

        PageHelper.startPage(pageNum, pageSize);

        Map<String,String> map = tokenUtil.parseToken(token);
        String username = map.get("userId");
        exam.setOperator(username);
        List<HbExam> list = hbExamMapper.selectExam(exam);

        return new PageInfo<>(list);
    }

    public void publishExam(HbExam exam){
        hbExamMapper.updateExam(exam);
    }

    public List<HbQuestionBank> selectRandomList(List<HbExamCreateType> hbExamCreateTypeList,
                                                 String token){

        List<HbQuestionBank> rlist = new ArrayList<>();
        if (hbExamCreateTypeList.size() < 1) {
            return rlist;
        }
        HbExamCreateType hbExamCreateType = new HbExamCreateType();
        for (int i=0; i<hbExamCreateTypeList.size(); i++) {
            hbExamCreateType = hbExamCreateTypeList.get(i);
            HbQuestionBank hbQuestionBank = new HbQuestionBank();
            hbQuestionBank.setQuestionDep(hbExamCreateType.getExamdep());
            hbQuestionBank.setQuestionDype(hbExamCreateType.getExamquestiontype());
            int size = hbExamCreateType.getExamquestionsize();

            List<HbQuestionBank> list = hbQuestionBankMapper.selectQuestionAndAns(hbQuestionBank);
            // 如果前台传入的数据小于数据库的条数，把查询的结果全部放入返回数组里
            if (list.size() < size) {
                rlist.addAll(list);
                continue;
            }

            Random random = new Random();
            boolean[] bool = new boolean[size]; //记录当前下标对应的随机数是否生成过
            int index = -1;
            for (int r=0; r<size; r++) {
                // 生成size位数的不重复随机数，用来获取题目
                do {
                    index = random.nextInt(size);
                }while(bool[index]);
                bool[index] = true;
                rlist.add(list.get(index));
            }
        }

        RedisConnection conn = null;
        try {
            conn = redisTemplate.getConnectionFactory().getConnection();
            // key值由登录的token与预览条件合并生成
            String rediskey = token + new String(ByteArrayUtils.objectToBytes(hbExamCreateTypeList).get());
            conn.set(rediskey.getBytes(), ByteArrayUtils.objectToBytes(rlist).get(), Expiration.seconds(300), RedisStringCommands.SetOption.SET_IF_ABSENT);
            conn.close();
        }catch (Exception e) {
            e.printStackTrace();
        }finally {
            if (!conn.isClosed()) {
                conn.close();
            }
        }

        return rlist;
    }

    @Transactional
    public boolean insertExam(List<HbExamCreateType> hbExamCreateTypeList,String token,String examName){
        if (hbExamCreateTypeList.size() <= 0) {
            return false;
        }
        RedisConnection conn = null;
        try {
            Map<String,String> map = tokenUtil.parseToken(token);
            String username = map.get("userId");;

            conn = redisTemplate.getConnectionFactory().getConnection();
            String rediskey = token + new String(ByteArrayUtils.objectToBytes(hbExamCreateTypeList).get());

            List<HbQuestionBank> redisL = new ArrayList<>();

            byte[] rByte = conn.get(rediskey.getBytes());
            // 如果缓存中没有查询出预览结果，则重新生成数据
            if (rByte == null || rByte.length <= 0) {
                redisL = selectRandomList(hbExamCreateTypeList,token);
            } else {
                redisL = (List<HbQuestionBank>) ByteArrayUtils.bytesToObject(conn.get(rediskey.getBytes())).get();
            }

            HbExam hbExam = new HbExam();
            String examCode = maxNoUtils.getIncrement("examCode","E",10);
            hbExam.setExamCode(examCode);
            hbExam.setExamName(examName);
            hbExam.setIspublic("01"); //待发布
            hbExam.setOperator(username);

            for (int i=0; i<hbExamCreateTypeList.size(); i++) {
                hbExamCreateTypeList.get(i).setExamcode(examCode);
                hbExamCreateTypeList.get(i).setOperater(username);
            }

            List<HbExamDetail> hbExamDetails = new ArrayList<>();
            HbExamDetail hbExamDetail = new HbExamDetail();
            for (int i=0; i<redisL.size(); i++) {
                hbExamDetail = new HbExamDetail();
                hbExamDetail.setExamcode(examCode);
                hbExamDetail.setQuestioncode(redisL.get(i).getQuestionCode());
                hbExamDetails.add(hbExamDetail);
            }
            hbExamCreateTypeMapper.insert(hbExamCreateTypeList);
            hbExamMapper.insert(hbExam);
            hbExamDetailMapper.insert(hbExamDetails);

            conn.del(rediskey.getBytes());
            conn.close();
        }catch (Exception e) {
            e.printStackTrace();
        }finally {
            if (!conn.isClosed()) {
                conn.close();
            }
        }
        return true;
    }

    public List<HbExamDetail> selectExamDetail(String examCode) {

        List<HbExamDetail> list = hbExamDetailMapper.selectExamDetailByExamCode(examCode);

        return list;
    }
}
